Mise à jour le 13/11/2021
Les types

Les types

Les types les plus courants en PHP sont les suivants :
* bool
* float (aussi appelé double)
* int
* string
* array
* object (qui représente l'abstraction de toutes les instances de classe)

Il existe aussi le non type null.

1. Le null

Le null est par définition ce qui est différent du non null. Ce qui est particulier avec le null, c'est qu'il y a une ambiguité car une variable à laquelle on associe une valeur nulle a également un type null.
Dès lors, si on définit une valeur et donc le type d'une variable, le null est une valeur qui ne devrait plus être permise.

1.1 Eviter le null en signature de méthode ?

Idéalement, moins il y a de types différents en retour de méthode, et plus simple sera l'utilisation de celle-ci par les utilisateurs, car les cas seront moins nombreux à gérer : il vaudrait donc mieux éviter d'utiliser le null dans son code car cela peut poser pas mal de problèmes lorsque la valeur nulle d'une variable se répand à travers les différentes méthodes. En pratique, cela ne simplifie pas pour autant le code et donc tout le reste (maintenabilité, temps d'exécution...).

Plutôt que d'écrire ceci :

function getMyArray()
{
    if (/* condition pouvant valoir false */) {
        return ['key' => 'value'];
    }

    return null;
}


On pourrait écrire cela :

function getMyArray()
{
    $myArray = []; 
    if (/* condition pouvant valoir false */) {
        $myArray = ['key' => 'value'];
    }

    return $myArray;
}

Ici, on garantit à l'utilisateur de la méthode getMyArray que le retour sera toujours un tableau, il pourra alors utiliser des fonctions d'itérations sur le retour de getMyArray sans avoir besoin de faire une vérification du retour comme is_array.

Evidemment, au niveau de l'exécution, la valeur affectée à myArray sera un tableau, donc cela consommera de fait plus de mémoire vive, mais ce point est clairement négligeable comparé au reste de la plupart des projets.

Là où cela devient moins pertinent de garantir un seul type de sortie, c'est dans le cas par exemple où la méthode doit chercher une entité stockée en base de données, il serait peut-être plus pertinent d'utiliser une méthode supplémentaire pour compter par exemple le nombre de cette entité en base. Cette dernière méthode pourrait en outre stocker dans un cache les données de l'entité pour épargner une requête supplémentaire à la base.

Voici une proposition, admettons qu'on ait cette méthode :

(findMyRessource($id) : myRessource);


Si on code avec l'usage du null, on aura plutôt ce genre de signature ci :

(findMyResource($id) : myResource|null);


et donc l'utilisateur sera obligé de tester que le retour ne vaut pas null afin d'utiliser cette ressource (par exemple : afficher une représentation de celle-ci à l'écran).

Il pourra faire ça de cette façon-là :

// Bout de code d'un controlleur Symfony qui récupère les infos de la ressource avant de la passer au template géré par Twig.
return $this->render('xxx.twig.html', [
    'myResource' => findMyResource($id)
]);

{# Code Twig #}
{% if myResource is not null %}
    <span>{{myResource.content}}</span>
{% else %}
     <span>No content</span>
{% endif %}


Ici, il doit gérer le fait que la ressource puisse avoir deux types différents (le null et object (myInstance)).

Pour éviter l'usage du null, on pourrait créer une autre méthode afin que la méthode findMyResource n'ait qu'un seul type de retour :

(myResourceExists($id) : bool);
(findMyResource($id) : myResource);


La première méthode retournera true ou false selon l'existence de la ressource, et la seconde méthode retournera la ressource en elle-même. Cependant, cela oblige de mettre des gardes fous dans les deux méthodes afin de retourner des Exceptions dans le cas où quelque chose se passe mal (récupération dans le cache par exemple qui ne fonctionnerait plus).

Et donc on aurait deux façons d'utiliser cela, soit directement dans le template :

$myResourceExists = myResourceExists($id);
if ($myResourceExists) {
    $myResource = findMyRessource($id);
}
return $this->render('xxx.twig.html', [
    'id' => $id,
]);

{# Code Twig #}
{% if myResourceExists(id) #}
    <span>{{findMyRessource($id).content}}</span>
{% else #}
    <span>No content</span>
{% endif #}

Cela demande au passage de créer deux fonctions Twigs.

Soit dans le controlleur :

$myResource = null;
$myResourceExists = myResourceExists($id);
if ($myResourceExists) {
    $myResource = findMyRessource($id);
}

return $this->render('xxx.twig.html', [
    'myResource' => $myResource,
]);

{# Code Twig #}
{% if myResource is not null #}
    <span>{{myResource.content}}</span>
{% else #}
    <span>No content</span>
{% endif #}


On voit ici les limites de la logique derrière l'idée de ne pas utiliser le null : on augmente le nombre de lignes de code, ainsi que leur complexité, en augmentant les risques de plantage

1.2 Comparer deux null entre eux

Ce genre de code par ailleurs n'a pas vraiment de sens :

$variableA = null;
$variableB = null;
$variableA === $variableB; // ceci vaut le bool true.


Car un null ne représentant rien, il est impossible de comparer deux nulls.

Exemple : Un non nombre de pommes dans un non sac.

$appleNumbers = null;
$bag = null;
$bag === $appleNumbers;


Ici, du point de vue de l'interpréteur, la dernière ligne vaudra true, mais on comprend assez facilement que fonctionnellement parlant, cela n'a aucun sens.

Par contre, si on déclare une variable nommée $null qui sert d'étalon (pour un test unitaire par exemple), ça aurait éventuellement un sens (en PhpUnit, on utiliserait le AssertNull voire un AssertTrue(\is_null($var))).